﻿
namespace WPF.TestEditor.Controls
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Threading;
    using ICSharpCode.AvalonEdit.Document;
    using WPF.TestEditor.Controls.AvalonTextEditor;

    public class AutoCompleteWindow : Window
    {
        readonly CompletionList completionList = new CompletionList("MetroCompletionList");
        public CompletionList CompletionList
        {
            get { return completionList; }
        }
        public TextBox Control { get; private set; }
        public bool CloseAutomatically { get; set; }
        
        private Window parentWindow;
        // Methods
        static AutoCompleteWindow()
        {
            Window.WindowStyleProperty.OverrideMetadata(typeof(AutoCompleteWindow), new FrameworkPropertyMetadata(WindowStyle.None));
        }
       // public TextBlock ltrInfo = new TextBlock();
       // public StackPanel pnlContent = new StackPanel();
        public AutoCompleteWindow(TextBox control)
        {
            if (control == null)
            {
                throw new ArgumentNullException("TextBox");
            }
            this.SetResourceReference(System.Windows.Controls.Control.StyleProperty, "AutoCompleteWindow");
            this.Control = control;
            this.CloseAutomatically = true;
            //this.SizeToContent = SizeToContent.WidthAndHeight;
            //this.MaxHeight = 300;
            //this.Width = 175;
            //pnlContent.Children.Add(ltrInfo); 
            //pnlContent.Children.Add(completionList);
            this.Content = completionList; 
            // prevent user from resizing window to 0x0
            //this.MinHeight = 15;
            // this.MinWidth = 30;
            
            //this.ShowInTaskbar = false;
            this.IsUp = true;
            this.WindowStartupLocation = System.Windows.WindowStartupLocation.Manual;
            
            this.parentWindow = Window.GetWindow(control);
            base.Owner = this.parentWindow;
            base.AddHandler(UIElement.MouseUpEvent, new MouseButtonEventHandler(this.OnMouseUp), true);
            this.StartOffset = this.EndOffset = this.Control.SelectionStart;
            this.AttachEvents();
            this.GotFocus += new RoutedEventHandler(AutoCompleteWindow_GotFocus);
            completionList.SizeChanged += new SizeChangedEventHandler(completionList_SizeChanged);
        }

        void AutoCompleteWindow_GotFocus(object sender, RoutedEventArgs e)
        {
            ActivateParentWindow();
        }

        void completionList_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdatePosition();
        }    
       


        protected virtual void AttachEvents()
        {
            Control.LostKeyboardFocus += new KeyboardFocusChangedEventHandler(this.Control_LostFocus);            
            Control.PreviewKeyDown += new KeyEventHandler(this.Control_KeyDown);
            if (this.parentWindow != null)
            {
                this.parentWindow.LocationChanged += new EventHandler(this.parentWindow_LocationChanged);
            }
            this.completionList.InsertionRequested += completionList_InsertionRequested;            
            
            this.Control.MouseWheel += Control_MouseWheel;
            this.Control.PreviewTextInput += Control_PreviewTextInput;
        }

        protected virtual void ActivateParentWindow()
        {
            if (this.parentWindow != null)
            {
                this.parentWindow.Activate();
            }
        }
        private void CloseIfFocusLost()
        {
            if ((this.CloseOnFocusLost && !base.IsActive) && !this.IsTextAreaFocused)
            {
                base.Close();
            }
        }

        protected virtual void DetachEvents()
        {
            Control.LostKeyboardFocus -= new KeyboardFocusChangedEventHandler(this.Control_LostFocus);           
            Control.PreviewKeyDown-= new KeyEventHandler(this.Control_KeyDown);
            if (this.parentWindow != null)
            {
                this.parentWindow.LocationChanged -= new EventHandler(this.parentWindow_LocationChanged);
            }
            this.completionList.InsertionRequested -= completionList_InsertionRequested;
            this.Control.MouseWheel -= Control_MouseWheel;
            this.Control.PreviewTextInput -= Control_PreviewTextInput;
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            this.DetachEvents();
        }

        protected override void OnDeactivated(EventArgs e)
        {
            base.OnDeactivated(e);
            base.Dispatcher.BeginInvoke(new Action(this.CloseIfFocusLost), DispatcherPriority.Background, new object[0]);
        }
        private void Control_KeyDown(object sender, KeyEventArgs e)
        {
            this.OnKeyDown(e);
            this.UpdatePosition();
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (!e.Handled && (e.Key == Key.Escape))
            {
                e.Handled = true;
                base.Close();
            }
            if (!e.Handled)
                CompletionList.HandleKey(e);
        }

        private void OnMouseUp(object sender, MouseButtonEventArgs e)
        {
            this.ActivateParentWindow();
        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            if (sizeInfo.HeightChanged && this.IsUp)
            {
                base.Top += sizeInfo.PreviousSize.Height - sizeInfo.NewSize.Height;
            }
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            this.UpdatePosition();
        }

        private void parentWindow_LocationChanged(object sender, EventArgs e)
        {
            this.UpdatePosition();
        }

        protected static bool RaiseEventPair(UIElement target, RoutedEvent previewEvent, RoutedEvent @event, RoutedEventArgs args)
        {
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }
            if (previewEvent == null)
            {
                throw new ArgumentNullException("previewEvent");
            }
            if (@event == null)
            {
                throw new ArgumentNullException("event");
            }
            if (args == null)
            {
                throw new ArgumentNullException("args");
            }
            args.RoutedEvent = previewEvent;
            target.RaiseEvent(args);
            args.RoutedEvent = @event;
            target.RaiseEvent(args);
            return args.Handled;
        }


        #region events
        private void Control_LostFocus(object sender, RoutedEventArgs e)
        {
            base.Dispatcher.BeginInvoke(new Action(this.CloseIfFocusLost), DispatcherPriority.Background, new object[0]);
        }

        void completionList_InsertionRequested(object sender, EventArgs e)
        {            
            // The window must close before Complete() is called.
            // If the Complete callback pushes stacked input handlers, we don't want to pop those when the CC window closes.
            var item = completionList.SelectedItem;
            if (item != null)
            {
                this.Control.Text = item.Text;
                this.Control.SelectionStart = item.Text.Length;
            }
            Close();
        }
        void Control_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            e.Handled = RaiseEventPair(this, PreviewTextInputEvent, TextInputEvent,
                                       new TextCompositionEventArgs(e.Device, e.TextComposition));           
        }

        void Control_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            e.Handled = RaiseEventPair(GetScrollEventTarget(),
                                       PreviewMouseWheelEvent, MouseWheelEvent,
                                       new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta));
        }
        #endregion

        UIElement GetScrollEventTarget()
        {
            if (completionList == null)
                return this;
            return completionList.ScrollViewer ?? completionList.ListBox ?? (UIElement)completionList;
        }

        protected void UpdatePosition()
        {
            Point location = Control.PointToScreen(new Point(0, Control.ActualHeight));
            //Size size = new Size(base.ActualWidth, base.ActualHeight);
            //Rect rect = new Rect(location, size);
            base.Left = location.X;
            base.Top = location.Y;
            base.Width = Control.ActualWidth;
            //ltrInfo.Text = string.Format("{0}:{1}\t{2}", location.X, location.Y, this.CompletionList.ListBox.Items.Count);
        }

        // Properties
        protected virtual bool CloseOnFocusLost
        {
            get
            {
                return true;
            }
        }

        public int EndOffset { get; set; }

        public bool ExpectInsertionBeforeStart { get; set; }

        private bool IsTextAreaFocused
        {
            get
            {
                if ((this.parentWindow != null) && !this.parentWindow.IsActive)
                {
                    return false;
                }
                return Control.IsKeyboardFocused;
            }
        }

        protected bool IsUp { get; private set; }

        public int StartOffset { get; set; }




    }



}
